Area Data V
NOTE: You can download the source files for this book from here. The source files are in the format of R Notebooks. Notebooks are pretty neat, because the allow you execute code within the notebook, so that you can work interactively with the notes.
In the previous chapter, you learned about how to use local spatial statistics for exploratory spatial data analysis.
If you wish to work interactively with this chapter you will need the following:
Learning Objectives
In this practice, you will:
- Practice how to estimate regression models in R.
- Learn about autocorrelation as a model diagnostic.
- Learn about variable transformations.
- Use autocorrelation analysis to improve regression models.
Suggested Readings
- Bailey TC and Gatrell AC [-@Bailey1995] Interactive Spatial Data Analysis, Chapter 7. Longman: Essex.
- Bivand RS, Pebesma E, and Gomez-Rubio V [-@Bivand2008] Applied Spatial Data Analysis with R, Chapter 9. Springer: New York.
- Brunsdon C and Comber L [-@Brunsdon2015R] An Introduction to R for Spatial Analysis and Mapping, Chapter 7. Sage: Los Angeles.
- O’Sullivan D and Unwin D [-@Osullivan2010] Geographic Information Analysis, 2nd Edition, Chapter 7. John Wiley & Sons: New Jersey.
Preliminaries
As usual, it is good practice to clear the working space to make sure that you do not have extraneous items there when you begin your work. The command in R to clear the workspace is rm (for “remove”), followed by a list of items to be removed. To clear the workspace from all objects, do the following:
rm(list = ls())
Note that ls() lists all objects currently on the worspace.
Load the libraries you will use in this activity:
library(tidyverse)
#library(rgdal)
#library(geosphere)
library(ggmap)
library(geosphere)
#library(gridExtra)
library(sf)
library(plotly)
library(spdep)
#library(crosstalk)
library(geog4ga3)
Next, read an object of class sf (simple feature) with the census tracts of Hamilton CMA and some selected population variables from the 2011 Census of Canada:
data(Hamilton_CT)
The sf object can be converted into a SpatialPolygonsDataFrame object for use with the spdedp package:
Hamilton_CT.sp <- as(Hamilton_CT, "Spatial")
Regression Analysis in R
This section provides a refresher on linear regression, before reviewing the estimation of regression models in R.
A linear regression model posits relationships between an outcome, called a dependent variable, and one or more covariates, called independent variables. Regression models capture statistical relationships, not causal relationships, but often the causality is implied by the choice of independent variables.
This is the form of a linear regression model: \[
y_i = \beta_0 + \sum_{j=1}^k{\beta_jx_{ij}} + \epsilon_i
\] where \(y_i\) is the dependent variable and \(x_ij\) (\(j=1,...,k\)) are the independent variables. The coefficients \(\beta\) are not known, but can be estimated from the data. And \(\epsilon_i\) is a term called a residual (or error), because it is the difference between the systematic term of the model and the value of \(y_i\): \[
\epsilon_i = y_i - \bigg(\beta_0 + \sum_{j=1}^k{\beta_jx_{ij}}\bigg)
\]
Estimation of a linear regression model is the procedure used to obtain values for the coefficients. This typically involves defining a loss function that needs to be minimized. In the case of linear regression, a widely used estimation procedure is least squares. This procedure allows a modeler to find the coefficients that minimize the sum of squared residuals. In very simple terms, the protocol is as follows: \[
\text{Find the values of }\beta\text{ that minimize }\sum_{i=1}^n{\epsilon_i^2}
\]
For this procedure to be valid, there are a few assumptions that need to be satisfied, including:
The functional form of the model is correct.
The independent variables are not collinear; this is often diagnosed by calculating the correlations among the independent variables, with values greater than 0.8 often being problematic.
The residuals have a mean of zero: \[
E[\epsilon_i|X]=0
\]
The residuals have constant variance: \[
Var[\epsilon_i|X] = \sigma^2 \text{ }\forall i
\]
The residuals are independent, that is, they are not correlated among them: \[
E[\epsilon_i\epsilon_j|X] = 0 \text{ }\forall i\neq j
\]
The last three assumptions ensure that the residuals are random. Violation of these assumptions is often a consequence of a failure in the first two (i.e., the model was not properly specified, and/or the residuals are not exogenous).
When all these assumptions are met, the coefficiens are said to be BLUE: Best Linear Unbiased Estimates - a desirable property because we wish to be able to quantify the relationships between covariates without bias.
The basic command for multivariate linear regression in R is lm(), for “linear model”. This is the help file of this function:
?lm #Remember that we can search the definition of a function by means of placing a question mark in front of the function itself.
Lets see how to estimate a model using this function.
To illustrate this, we will use the example of population density gradients. These gradients are representations of the variation of population density in cities, and are of interest because they are related to land rent, urban form, and commuting patterns, among other things (see accompanying reading for more information).
Urban economic theory suggests that population density declines with distance from the central business district of a city, or its CBD. This leads to the following model, where the population density at \(i\) is a function of the distance of \(i\) to the CBD. Since this is likely a stochastic process, we allow for some randomness by means of the residuals: \[
P_i = f(D_i) + \epsilon_i
\]
Lets add distance to the CBD as a covariate to your dataframe. We will use Jackson Square as the CBD of Hamilton:
xy_cbd <- c(-79.8708, 43.2584)
?c
Convert these coordinates to sf, with EPSG code 4326 for longitude and latitude:
#xy_cbd <- st_as_sf(xy_cbd, coords = c("lon", "lat"), crs = 4326)
xy_ct <- coordinates(spTransform(Hamilton_CT.sp, CRSobj = "+proj=longlat +datum=WGS84 +no_defs"))
#we use 'spTransform' as a method for map projection.
?coordinates
Given these coordinates, the function sf::st_distance can be used to calculate the great circle distance between the centroids of the census tracts and Hamilton’s CBD. Call this dist2cbd.sl, i.e., straight line distance to CBD in a straight:
dist2cbd.sl <- distGeo(xy_ct, xy_cbd)
?distGeo
Add the distance to Hamilton_CT for analysis:
Hamilton_CT$dist.sl <- dist2cbd.sl
Regression analysis is implemented in R by means of the lm function. The arguments of the model include an object of type “formula” and a dataframe. Other arguments include conditions for subsetting the data, using sampling weights, and so on.
A formula is written in the form y ~ x1 + x2, and more complex expressions are possible too, as we will see below. For the time being, the formula is simply POP_DENSIT ~ dist.sl:
model1 <- lm(formula = POP_DENSITY ~ dist.sl, data = Hamilton_CT)
summary(model1)
Call:
lm(formula = POP_DENSITY ~ dist.sl, data = Hamilton_CT)
Residuals:
Min 1Q Median 3Q Max
-3841.2 -1338.3 -177.1 950.9 10009.1
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4405.15414 250.16355 17.609 < 2e-16 ***
dist.sl -0.17989 0.02418 -7.439 3.63e-12 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1892 on 186 degrees of freedom
Multiple R-squared: 0.2293, Adjusted R-squared: 0.2251
F-statistic: 55.33 on 1 and 186 DF, p-value: 3.631e-12
The value of the function is an object of class lm that contains the results of the estimation, including the coefficients with their significance diagnostics, and the coefficient of multiple determination, among other items.
Notice how the coefficient for distance is negative (and significant). This indicates that population density declines with increasing distance: \[
P_i = f(D_i) + \epsilon_i = 4405.15414 - 0.17989D_i + \epsilon_i
\]
Autocorrelation as a Model Diagnostic
Let quickly explore the fit of the model. The scatterplot is of the actual population density and the distance to CBD, whereas the blue line is the regression model:
ggplot(data = Hamilton_CT, aes(x = dist.sl, y = POP_DENSITY)) +
geom_point() +
geom_abline(slope = model1$coefficients[2],
intercept = model1$coefficients[1],
color = "blue", size = 1) +
geom_vline(xintercept = 0) + geom_hline(yintercept = 0)

Clearly, there is a fair amount of noise (the scatter of the dots around the regression line). In this case, the regression line captures the general trend of the data, but underestimates most of the high population density areas, and overestimates most of the low population areas.
If the pattern of under- and over-estimation is random (i.e., the residuals are random), that would indicate that the model successfully retrieved all the systematic pattern. If the pattern is not random, there is a violation of assumption of independence.
Lets add the residuals of the model to your dataframes:
Hamilton_CT$model1.e <- model1$residuals
And now we can create a map of the residuals, with red indicating negative residuals and blue positive:
plot_ly(Hamilton_CT) %>% #Recall 'plot.ly' is an interactive mapping tool
add_sf(color = ~(model1.e > 0), colors = c("red", "dodgerblue4")) #You can add characteristics to a plotly map by means of the 'add_sf' function. This example adds colours to represent positive and negative residuals
Does the distribution of residuals look random?
Fortunately, we already have the tools to help us with this question. Lets create a set of spatial weights:
Hamilton_CT.w <- nb2listw(poly2nb(Hamilton_CT.sp)) #Recall that we can create a lsit of spatial weights of nearest neighbours using 'nb2listw'
?poly2nb
With this, we can calculate Moran’s I:
moran.test(Hamilton_CT$model1.e, Hamilton_CT.w)
Moran I test under randomisation
data: Hamilton_CT$model1.e
weights: Hamilton_CT.w
Moran I statistic standard deviate = 9.7447, p-value < 2.2e-16
alternative hypothesis: greater
sample estimates:
Moran I statistic Expectation Variance
0.395382878 -0.005347594 0.001691092
This supports the visual inspection of the map, since the test of hypothesis rejects the null hypothesis of independence at a very high level of confidence (note the extremely small value of p).
Spatial autocorrelation, as mentioned above, is a violation of a key assumption of linear regression, and likely the consequence of a model that was not correctly specified, either because the functional form was incorrect (e.g., the relationship was not linear), or there are missing covariates.
Lets explore the first of these possibilities by means of variable transformations.
Variable Transformations
The term linear regression refers to the linearity in the coefficients. Variable transformations allow you to consider non-linear relationships between covariates, while still preserving the linearity of the coefficients.
For instance, a possible transformation of the variable distance could be as inverse distance: \[
f(D_i) = \beta_0 + \beta_1\frac{1}{D_i}
\]
Lets create a new covariate that is the inverse distance:
Hamilton_CT <- mutate(Hamilton_CT, invdist.sl = 1/dist.sl)
#Recall that mutate adds to existing variables while preserving those that already exist. We are mutating Hamilton census trat information to create inverse distance
Use inverse distance as the covariate in the model:
model2 <- lm(formula = POP_DENSITY ~ invdist.sl, data = Hamilton_CT)
summary(model2)
Call:
lm(formula = POP_DENSITY ~ invdist.sl, data = Hamilton_CT)
Residuals:
Min 1Q Median 3Q Max
-6763 -1375 -52 1108 9675
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2299.6 164.4 13.988 < 2e-16 ***
invdist.sl 2260521.5 342039.7 6.609 3.97e-10 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1940 on 186 degrees of freedom
Multiple R-squared: 0.1902, Adjusted R-squared: 0.1858
F-statistic: 43.68 on 1 and 186 DF, p-value: 3.965e-10
As the scatterplot below shows (the model is the blue line), we can capture a non-linear relationship, which does a better job of describing the high density of tracts close to the CBD, but unfortunately is a poor description of density almost everywhere else:
ggplot(data = Hamilton_CT, aes(x = dist.sl, y = POP_DENSITY)) +
geom_point() +
stat_function(fun=function(x)2299.6 + 2260521.5/x, geom="line", color = "blue", size = 1) +
geom_vline(xintercept = 0) + geom_hline(yintercept = 0)

Add the residuals of this model to the dataframe for further examination:
Hamilton_CT$model2.e <- model2$residuals
If we calculate Moran’s I, you will notice that the coefficient is lower than for the previous model but still significant, wich means that the residuals are not random:
moran.test(Hamilton_CT$model2.e, Hamilton_CT.w)
Moran I test under randomisation
data: Hamilton_CT$model2.e
weights: Hamilton_CT.w
Moran I statistic standard deviate = 8.8236, p-value < 2.2e-16
alternative hypothesis: greater
sample estimates:
Moran I statistic Expectation Variance
0.358132814 -0.005347594 0.001696970
The literature on population density gradients suggests other non-linear transformations, including: \[
f(D_i) = exp(\beta_0)exp(\beta_1x_i)
\]
This function is no longer linear in the coefficients (since all the coefficientsare transformed by the exponential). Fortunately, there is a simple way of changing this to a linear expression, by taking the logarithm on both sides of the equation: \[
ln(P_i) = \beta_0 + \beta_1x_i + \epsilon_i
\]
Lets create these covariates:
Hamilton_CT <- mutate(Hamilton_CT, lnPOP_DEN = log(POP_DENSITY))
And estimate the model:
model3 <- lm(formula = lnPOP_DEN ~ dist.sl, data = Hamilton_CT)
summary(model3)
Call:
lm(formula = lnPOP_DEN ~ dist.sl, data = Hamilton_CT)
Residuals:
Min 1Q Median 3Q Max
-5.5857 -0.3395 0.2970 0.6897 2.4224
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 8.465e+00 1.588e-01 53.294 < 2e-16 ***
dist.sl -1.161e-04 1.536e-05 -7.561 1.77e-12 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.202 on 186 degrees of freedom
Multiple R-squared: 0.2351, Adjusted R-squared: 0.231
F-statistic: 57.18 on 1 and 186 DF, p-value: 1.77e-12
ggplot(data = Hamilton_CT, aes(x = dist.sl, y = POP_DENSITY)) +
geom_point() +
stat_function(fun=function(x)exp(8.465 - 0.0001161 * x),
geom="line", color = "blue", size = 1) +
geom_vline(xintercept = 0) + geom_hline(yintercept = 0)

As before, add the residuals of the model to the dataframe for further examination:
Hamilton_CT$model3.e <- model3$residuals
While this latest model provides a somewhat better fit, there is still systematic under- and over-prediction, as seen in the map below (red are negative residuals and blue are positive):
plot_ly(Hamilton_CT) %>%
add_sf(color = ~(model3.e > 0), colors = c("red", "dodgerblue4"))
Moran’s I as well strongly suggests that the residuals are not independent:
moran.test(Hamilton_CT$model3.e, Hamilton_CT.w)
Moran I test under randomisation
data: Hamilton_CT$model3.e
weights: Hamilton_CT.w
Moran I statistic standard deviate = 8.0156, p-value = 5.478e-16
alternative hypothesis: greater
sample estimates:
Moran I statistic Expectation Variance
0.325928113 -0.005347594 0.001708056
A Note about Spatial Autocorrelation in Regression Analysis
Spatial autocorrelation was originally seen as a problem in regression analysis. It is not difficult to see why, after testing three models in this chapter.
I prefer to view spatial autocorrelation as an opportunity for discovery. For instance, the models above all seem to struggle capturing the large variations in population density between the central parts of the city and the suburbs of Hamilton. Perhaps this could be due to a regime change, or in other words, the presence of an underlying process that operates somewhat differently in different parts parts of the city. The last model, for instance, suggests that the close proximity of Burlington might have an effect.
The analysis that follows is somewhat more advanced, but serves to illustrate the idea of spatial autocorrelation as a tool for discovery.
Lets begin by creating local Moran maps to identify potential spatial regimes:
localmoran.map(Hamilton_CT, Hamilton_CT.w, "POP_DENSITY", by = "TRACT")
Examination of the map above, suggests that there are possibly three regimes: a CBD (“HH” and significant tracts), Suburbs (“LL” and significant tracts), and Other (not significant tracts). Based on this, we will create two indicator variables, one for census tracts in the CBD and another for census tracts in the Suburbs. An indicator variable takes values of 1 or zero, depending on whether a condition is true. For instance, all census tracts in the CBD will take the value of 1 in the CBD indicator variable, and all others will be zero.
Begin by computing the local statistics:
POP_DEN.lm <- localmoran(Hamilton_CT$POP_DENSITY, listw = Hamilton_CT.w)
Next, identify the type of tract based on the spatial relationships (i.e., “HH”, “LL”, or “HL/LH”). After that, identify as CBD all tracts for which Type is “HH” and the p-value is less than or equal to 0.05. Likewise, identify as Suburb all tracts for which Type is “LL” and the p-value is also less than or equal to 0.05:
df_msc <- transmute(Hamilton_CT,
TRACT = TRACT,
Z = (POP_DENSITY - mean(POP_DENSITY)) / var(POP_DENSITY),
SMA = lag.listw(Hamilton_CT.w, Z),
Type = factor(ifelse(Z < 0 & SMA < 0, "LL",
ifelse(Z > 0 & SMA > 0, "HH", "HL/LH"))))
df_msc <- cbind(df_msc, POP_DEN.lm)
CBD <- ifelse(df_msc$Type == "HH" & df_msc$`Pr.z...0` < 0.05, 1, 0)
Suburb <- ifelse(df_msc$Type == "LL" & df_msc$`Pr.z...0` < 0.05, 1, 0)
Add the indicator variables to the dataframe:
Hamilton_CT$CBD <- CBD
Hamilton_CT$Suburb <- Suburb
The model that I propose to estimate is a variation of the last non-linear specification, but with regime breaks: \[
ln(P_i) = \beta_0 + \beta_1x_i + \beta_2CBD_i + \beta_3Suburb_i + \beta_4CBD_ix_i + \beta_5Suburb_ix_i + \epsilon_i
\]
Since the indicator variables for CBD and Suburb take values of zero and one, effectively we have the following: \[
ln(P_i)=\Bigg\{\begin{array}{l l}
(\beta_0 + \beta_2) + (\beta_1 + \beta_2)x_i + \epsilon_i \text{ if census tract } i \text{ is in the CBD}\\
(\beta_0 + \beta_3) + (\beta_1 + \beta_5)x_i + \epsilon_i \text{ if census tract } i \text{ is in the Suburbs}\\
\beta_0 + \beta_1x_i + \epsilon_i \text{ otherwise}\\
\end{array}
\]
Notice how the model now allows for different slopes and intercepts for observations in different parts of the city. Estimate the model:
model4 <- lm(formula = lnPOP_DEN ~ dist.sl + CBD * dist.sl + Suburb * dist.sl,
data = Hamilton_CT)
summary(model4)
Call:
lm(formula = lnPOP_DEN ~ dist.sl + CBD * dist.sl + Suburb * dist.sl,
data = Hamilton_CT)
Residuals:
Min 1Q Median 3Q Max
-6.4523 -0.2128 0.1806 0.4907 2.3234
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.943e+00 1.571e-01 50.577 < 2e-16 ***
dist.sl -3.248e-05 1.561e-05 -2.081 0.03880 *
CBD 9.536e-01 3.788e-01 2.518 0.01267 *
Suburb -9.535e-01 5.089e-01 -1.874 0.06260 .
dist.sl:CBD -2.730e-05 1.410e-04 -0.194 0.84669
dist.sl:Suburb -1.006e-04 3.556e-05 -2.828 0.00521 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.9384 on 182 degrees of freedom
Multiple R-squared: 0.5435, Adjusted R-squared: 0.5309
F-statistic: 43.33 on 5 and 182 DF, p-value: < 2.2e-16
The model is a much better fit (see the the coefficient of multiple determination).
Lets examine the residuals:
Hamilton_CT$model4.e <- model4$residuals
plot_ly(Hamilton_CT) %>%
add_sf(color = ~(model4.e > 0), colors = c("red", "dodgerblue4"))
It is not clear from the visual inspection that the residuals are independent, but this can be tested as usual by means of Moran’s I coefficient:
moran.test(Hamilton_CT$model4.e, Hamilton_CT.w)
Moran I test under randomisation
data: Hamilton_CT$model4.e
weights: Hamilton_CT.w
Moran I statistic standard deviate = 0.74498, p-value = 0.2281
alternative hypothesis: greater
sample estimates:
Moran I statistic Expectation Variance
0.024432414 -0.005347594 0.001597957
Based on the results, we fail to reject the null hypothesis, and can be confident that the residuals are likely random.
The following figure illustrates the final model:
fun.1 <- function(x)exp(7.943 + 0.9536 - (0.00003248 + 0.0000273) * x) #CBD
fun.2 <- function(x)exp(7.943 - 0.9535 - (0.00003248 + 0.0001006) * x) #Suburb
fun.3 <- function(x)exp(7.943 - 0.00003248 * x) #Other
ggplot(data = Hamilton_CT, aes(x = dist.sl, y = POP_DENSITY)) +
geom_point() +
geom_point(data = filter(Hamilton_CT, CBD == 1), color = "Red") +
geom_point(data = filter(Hamilton_CT, Suburb == 1), color = "Blue") +
stat_function(fun= fun.1,
geom="line", size = 1, aes(color = "CBD")) +
stat_function(fun=fun.2,
geom="line", size = 1, aes(color = "Suburb")) +
stat_function(fun=fun.3,
geom="line", size = 1, aes(color = "Other")) +
scale_color_manual(values = c("red", "black", "blue"),
labels = c("CBD", "Other", "Suburb")) +
geom_vline(xintercept = 0) + geom_hline(yintercept = 0)

LS0tCnRpdGxlOiAiMTQgQXJlYSBEYXRhIFYiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgQXJlYSBEYXRhIFYKCipOT1RFKjogWW91IGNhbiBkb3dubG9hZCB0aGUgc291cmNlIGZpbGVzIGZvciB0aGlzIGJvb2sgZnJvbSBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL3BhZXpoYS9TcGF0aWFsLVN0YXRpc3RpY3MtQ291cnNlKS4gVGhlIHNvdXJjZSBmaWxlcyBhcmUgaW4gdGhlIGZvcm1hdCBvZiBSIE5vdGVib29rcy4gTm90ZWJvb2tzIGFyZSBwcmV0dHkgbmVhdCwgYmVjYXVzZSB0aGUgYWxsb3cgeW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCBzbyB0aGF0IHlvdSBjYW4gd29yayBpbnRlcmFjdGl2ZWx5IHdpdGggdGhlIG5vdGVzLiAKCkluIHRoZSBwcmV2aW91cyBjaGFwdGVyLCB5b3UgbGVhcm5lZCBhYm91dCBob3cgdG8gdXNlIGxvY2FsIHNwYXRpYWwgc3RhdGlzdGljcyBmb3IgZXhwbG9yYXRvcnkgc3BhdGlhbCBkYXRhIGFuYWx5c2lzLiAKCklmIHlvdSB3aXNoIHRvIHdvcmsgaW50ZXJhY3RpdmVseSB3aXRoIHRoaXMgY2hhcHRlciB5b3Ugd2lsbCBuZWVkIHRoZSBmb2xsb3dpbmc6CgoqIEFuIFIgbWFya2Rvd24gbm90ZWJvb2sgdmVyc2lvbiBvZiB0aGlzIGRvY3VtZW50ICh0aGUgc291cmNlIGZpbGUpLgoKKiBBIHBhY2thZ2UgY2FsbGVkIGBnZW9nNGdhM2AuCgojIyBMZWFybmluZyBPYmplY3RpdmVzCgpJbiB0aGlzIHByYWN0aWNlLCB5b3Ugd2lsbDoKCjEuIFByYWN0aWNlIGhvdyB0byBlc3RpbWF0ZSByZWdyZXNzaW9uIG1vZGVscyBpbiBSLgoyLiBMZWFybiBhYm91dCBhdXRvY29ycmVsYXRpb24gYXMgYSBtb2RlbCBkaWFnbm9zdGljLgozLiBMZWFybiBhYm91dCB2YXJpYWJsZSB0cmFuc2Zvcm1hdGlvbnMuCjQuIFVzZSBhdXRvY29ycmVsYXRpb24gYW5hbHlzaXMgdG8gaW1wcm92ZSByZWdyZXNzaW9uIG1vZGVscy4KCiMjIFN1Z2dlc3RlZCBSZWFkaW5ncwoKLSBCYWlsZXkgVEMgYW5kIEdhdHJlbGwgQUMgWy1AQmFpbGV5MTk5NV0gSW50ZXJhY3RpdmUgU3BhdGlhbCBEYXRhIEFuYWx5c2lzLCBDaGFwdGVyIDcuIExvbmdtYW46IEVzc2V4LgotIEJpdmFuZCBSUywgUGViZXNtYSBFLCBhbmQgR29tZXotUnViaW8gViBbLUBCaXZhbmQyMDA4XSBBcHBsaWVkIFNwYXRpYWwgRGF0YSBBbmFseXNpcyB3aXRoIFIsIENoYXB0ZXIgOS4gU3ByaW5nZXI6IE5ldyBZb3JrLgotIEJydW5zZG9uIEMgYW5kIENvbWJlciBMIFstQEJydW5zZG9uMjAxNVJdIEFuIEludHJvZHVjdGlvbiB0byBSIGZvciBTcGF0aWFsIEFuYWx5c2lzIGFuZCBNYXBwaW5nLCBDaGFwdGVyIDcuIFNhZ2U6IExvcyBBbmdlbGVzLgotIE8nU3VsbGl2YW4gRCBhbmQgVW53aW4gRCBbLUBPc3VsbGl2YW4yMDEwXSBHZW9ncmFwaGljIEluZm9ybWF0aW9uIEFuYWx5c2lzLCAybmQgRWRpdGlvbiwgQ2hhcHRlciA3LiBKb2huIFdpbGV5ICYgU29uczogTmV3IEplcnNleS4KCiMjIFByZWxpbWluYXJpZXMKCkFzIHVzdWFsLCBpdCBpcyBnb29kIHByYWN0aWNlIHRvIGNsZWFyIHRoZSB3b3JraW5nIHNwYWNlIHRvIG1ha2Ugc3VyZSB0aGF0IHlvdSBkbyBub3QgaGF2ZSBleHRyYW5lb3VzIGl0ZW1zIHRoZXJlIHdoZW4geW91IGJlZ2luIHlvdXIgd29yay4gVGhlIGNvbW1hbmQgaW4gUiB0byBjbGVhciB0aGUgd29ya3NwYWNlIGlzIGBybWAgKGZvciAicmVtb3ZlIiksIGZvbGxvd2VkIGJ5IGEgbGlzdCBvZiBpdGVtcyB0byBiZSByZW1vdmVkLiBUbyBjbGVhciB0aGUgd29ya3NwYWNlIGZyb20gX2FsbF8gb2JqZWN0cywgZG8gdGhlIGZvbGxvd2luZzoKYGBge3J9CnJtKGxpc3QgPSBscygpKQpgYGAKCk5vdGUgdGhhdCBgbHMoKWAgbGlzdHMgYWxsIG9iamVjdHMgY3VycmVudGx5IG9uIHRoZSB3b3JzcGFjZS4KCkxvYWQgdGhlIGxpYnJhcmllcyB5b3Ugd2lsbCB1c2UgaW4gdGhpcyBhY3Rpdml0eToKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQojbGlicmFyeShyZ2RhbCkKI2xpYnJhcnkoZ2Vvc3BoZXJlKQpsaWJyYXJ5KGdnbWFwKQpsaWJyYXJ5KGdlb3NwaGVyZSkKI2xpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShzcGRlcCkKI2xpYnJhcnkoY3Jvc3N0YWxrKQpsaWJyYXJ5KGdlb2c0Z2EzKQpgYGAKCk5leHQsIHJlYWQgYW4gb2JqZWN0IG9mIGNsYXNzIGBzZmAgKHNpbXBsZSBmZWF0dXJlKSB3aXRoIHRoZSBjZW5zdXMgdHJhY3RzIG9mIEhhbWlsdG9uIENNQSBhbmQgc29tZSBzZWxlY3RlZCBwb3B1bGF0aW9uIHZhcmlhYmxlcyBmcm9tIHRoZSAyMDExIENlbnN1cyBvZiBDYW5hZGE6CmBgYHtyfQpkYXRhKEhhbWlsdG9uX0NUKQpgYGAKClRoZSBgc2ZgIG9iamVjdCBjYW4gYmUgY29udmVydGVkIGludG8gYSBgU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lYCBvYmplY3QgZm9yIHVzZSB3aXRoIHRoZSBgc3BkZWRwYCBwYWNrYWdlOgpgYGB7cn0KSGFtaWx0b25fQ1Quc3AgPC0gYXMoSGFtaWx0b25fQ1QsICJTcGF0aWFsIikKYGBgCgojIyBSZWdyZXNzaW9uIEFuYWx5c2lzIGluIFIKClRoaXMgc2VjdGlvbiBwcm92aWRlcyBhIHJlZnJlc2hlciBvbiBsaW5lYXIgcmVncmVzc2lvbiwgYmVmb3JlIHJldmlld2luZyB0aGUgZXN0aW1hdGlvbiBvZiByZWdyZXNzaW9uIG1vZGVscyBpbiBgUmAuCgpBIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHBvc2l0cyByZWxhdGlvbnNoaXBzIGJldHdlZW4gYW4gb3V0Y29tZSwgY2FsbGVkIGEgZGVwZW5kZW50IHZhcmlhYmxlLCBhbmQgb25lIG9yIG1vcmUgY292YXJpYXRlcywgY2FsbGVkIGluZGVwZW5kZW50IHZhcmlhYmxlcy4gUmVncmVzc2lvbiBtb2RlbHMgY2FwdHVyZSBzdGF0aXN0aWNhbCByZWxhdGlvbnNoaXBzLCBub3QgY2F1c2FsIHJlbGF0aW9uc2hpcHMsIGJ1dCBvZnRlbiB0aGUgY2F1c2FsaXR5IGlzIGltcGxpZWQgYnkgdGhlIGNob2ljZSBvZiBpbmRlcGVuZGVudCB2YXJpYWJsZXMuCgpUaGlzIGlzIHRoZSBmb3JtIG9mIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWw6CiQkCnlfaSA9IFxiZXRhXzAgKyBcc3VtX3tqPTF9Xmt7XGJldGFfanhfe2lqfX0gKyBcZXBzaWxvbl9pCiQkCndoZXJlICR5X2kkIGlzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgYW5kICR4X2lqJCAoJGo9MSwuLi4sayQpIGFyZSB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzLiBUaGUgY29lZmZpY2llbnRzICRcYmV0YSQgYXJlIG5vdCBrbm93biwgYnV0IGNhbiBiZSBlc3RpbWF0ZWQgZnJvbSB0aGUgZGF0YS4gQW5kICRcZXBzaWxvbl9pJCBpcyBhIHRlcm0gY2FsbGVkIGEgX3Jlc2lkdWFsXyAob3IgX2Vycm9yXyksIGJlY2F1c2UgaXQgaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgc3lzdGVtYXRpYyB0ZXJtIG9mIHRoZSBtb2RlbCBhbmQgdGhlIHZhbHVlIG9mICR5X2kkOgokJApcZXBzaWxvbl9pID0geV9pIC0gXGJpZ2coXGJldGFfMCArIFxzdW1fe2o9MX1ea3tcYmV0YV9qeF97aWp9fVxiaWdnKQokJAoKRXN0aW1hdGlvbiBvZiBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGlzIHRoZSBwcm9jZWR1cmUgdXNlZCB0byBvYnRhaW4gdmFsdWVzIGZvciB0aGUgY29lZmZpY2llbnRzLiBUaGlzIHR5cGljYWxseSBpbnZvbHZlcyBkZWZpbmluZyBhIF9sb3NzIGZ1bmN0aW9uXyB0aGF0IG5lZWRzIHRvIGJlIG1pbmltaXplZC4gSW4gdGhlIGNhc2Ugb2YgbGluZWFyIHJlZ3Jlc3Npb24sIGEgd2lkZWx5IHVzZWQgZXN0aW1hdGlvbiBwcm9jZWR1cmUgaXMgX2xlYXN0IHNxdWFyZXNfLiBUaGlzIHByb2NlZHVyZSBhbGxvd3MgYSBtb2RlbGVyIHRvIGZpbmQgdGhlIGNvZWZmaWNpZW50cyB0aGF0IG1pbmltaXplIHRoZSBzdW0gb2Ygc3F1YXJlZCByZXNpZHVhbHMuIEluIHZlcnkgc2ltcGxlIHRlcm1zLCB0aGUgcHJvdG9jb2wgaXMgYXMgZm9sbG93czoKJCQKXHRleHR7RmluZCB0aGUgdmFsdWVzIG9mIH1cYmV0YVx0ZXh0eyB0aGF0IG1pbmltaXplIH1cc3VtX3tpPTF9Xm57XGVwc2lsb25faV4yfQokJAoKRm9yIHRoaXMgcHJvY2VkdXJlIHRvIGJlIHZhbGlkLCB0aGVyZSBhcmUgYSBmZXcgYXNzdW1wdGlvbnMgdGhhdCBuZWVkIHRvIGJlIHNhdGlzZmllZCwgaW5jbHVkaW5nOgoKMSkgVGhlIGZ1bmN0aW9uYWwgZm9ybSBvZiB0aGUgbW9kZWwgaXMgY29ycmVjdC4KCjIpIFRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgYXJlIG5vdCBjb2xsaW5lYXI7IHRoaXMgaXMgb2Z0ZW4gZGlhZ25vc2VkIGJ5IGNhbGN1bGF0aW5nIHRoZSBjb3JyZWxhdGlvbnMgYW1vbmcgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcywgd2l0aCB2YWx1ZXMgZ3JlYXRlciB0aGFuIDAuOCBvZnRlbiBiZWluZyBwcm9ibGVtYXRpYy4KCjMpIFRoZSByZXNpZHVhbHMgaGF2ZSBhIG1lYW4gb2YgemVybzoKJCQKRVtcZXBzaWxvbl9pfFhdPTAKJCQKCjQpIFRoZSByZXNpZHVhbHMgaGF2ZSBjb25zdGFudCB2YXJpYW5jZToKJCQKVmFyW1xlcHNpbG9uX2l8WF0gPSBcc2lnbWFeMiBcdGV4dHsgfVxmb3JhbGwgaQokJAoKNSkgVGhlIHJlc2lkdWFscyBhcmUgaW5kZXBlbmRlbnQsIHRoYXQgaXMsIHRoZXkgYXJlIG5vdCBjb3JyZWxhdGVkIGFtb25nIHRoZW06CiQkCkVbXGVwc2lsb25faVxlcHNpbG9uX2p8WF0gPSAwIFx0ZXh0eyB9XGZvcmFsbCBpXG5lcSBqCiQkCgpUaGUgbGFzdCB0aHJlZSBhc3N1bXB0aW9ucyBlbnN1cmUgdGhhdCB0aGUgcmVzaWR1YWxzIGFyZSBfcmFuZG9tXy4gVmlvbGF0aW9uIG9mIHRoZXNlIGFzc3VtcHRpb25zIGlzIG9mdGVuIGEgY29uc2VxdWVuY2Ugb2YgYSBmYWlsdXJlIGluIHRoZSBmaXJzdCB0d28gKGkuZS4sIHRoZSBtb2RlbCB3YXMgbm90IHByb3Blcmx5IHNwZWNpZmllZCwgYW5kL29yIHRoZSByZXNpZHVhbHMgYXJlIG5vdCBleG9nZW5vdXMpLgoKV2hlbiBhbGwgdGhlc2UgYXNzdW1wdGlvbnMgYXJlIG1ldCwgdGhlIGNvZWZmaWNpZW5zIGFyZSBzYWlkIHRvIGJlIF9CTFVFXzogQmVzdCBMaW5lYXIgVW5iaWFzZWQgRXN0aW1hdGVzIC0gYSBkZXNpcmFibGUgcHJvcGVydHkgYmVjYXVzZSB3ZSB3aXNoIHRvIGJlIGFibGUgdG8gcXVhbnRpZnkgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBjb3ZhcmlhdGVzIHdpdGhvdXQgYmlhcy4KClRoZSBiYXNpYyBjb21tYW5kIGZvciBtdWx0aXZhcmlhdGUgbGluZWFyIHJlZ3Jlc3Npb24gaW4gUiBpcyBgbG0oKWAsIGZvciAibGluZWFyIG1vZGVsIi4gVGhpcyBpcyB0aGUgaGVscCBmaWxlIG9mIHRoaXMgZnVuY3Rpb246CmBgYHtyfQo/bG0gI1JlbWVtYmVyIHRoYXQgd2UgY2FuIHNlYXJjaCB0aGUgZGVmaW5pdGlvbiBvZiBhIGZ1bmN0aW9uIGJ5IG1lYW5zIG9mIHBsYWNpbmcgYSBxdWVzdGlvbiBtYXJrIGluIGZyb250IG9mIHRoZSBmdW5jdGlvbiBpdHNlbGYuIApgYGAKCkxldHMgc2VlIGhvdyB0byBlc3RpbWF0ZSBhIG1vZGVsIHVzaW5nIHRoaXMgZnVuY3Rpb24uIAoKVG8gaWxsdXN0cmF0ZSB0aGlzLCB3ZSB3aWxsIHVzZSB0aGUgZXhhbXBsZSBvZiBwb3B1bGF0aW9uIGRlbnNpdHkgZ3JhZGllbnRzLiBUaGVzZSBncmFkaWVudHMgYXJlIHJlcHJlc2VudGF0aW9ucyBvZiB0aGUgdmFyaWF0aW9uIG9mIHBvcHVsYXRpb24gZGVuc2l0eSBpbiBjaXRpZXMsIGFuZCBhcmUgb2YgaW50ZXJlc3QgYmVjYXVzZSB0aGV5IGFyZSByZWxhdGVkIHRvIGxhbmQgcmVudCwgdXJiYW4gZm9ybSwgYW5kIGNvbW11dGluZyBwYXR0ZXJucywgYW1vbmcgb3RoZXIgdGhpbmdzIChzZWUgYWNjb21wYW55aW5nIHJlYWRpbmcgZm9yIG1vcmUgaW5mb3JtYXRpb24pLgoKVXJiYW4gZWNvbm9taWMgdGhlb3J5IHN1Z2dlc3RzIHRoYXQgcG9wdWxhdGlvbiBkZW5zaXR5IGRlY2xpbmVzIHdpdGggZGlzdGFuY2UgZnJvbSB0aGUgY2VudHJhbCBidXNpbmVzcyBkaXN0cmljdCBvZiBhIGNpdHksIG9yIGl0cyBDQkQuIFRoaXMgbGVhZHMgdG8gdGhlIGZvbGxvd2luZyBtb2RlbCwgd2hlcmUgdGhlIHBvcHVsYXRpb24gZGVuc2l0eSBhdCAkaSQgaXMgYSBmdW5jdGlvbiBvZiB0aGUgZGlzdGFuY2Ugb2YgJGkkIHRvIHRoZSBDQkQuIFNpbmNlIHRoaXMgaXMgbGlrZWx5IGEgc3RvY2hhc3RpYyBwcm9jZXNzLCB3ZSBhbGxvdyBmb3Igc29tZSByYW5kb21uZXNzIGJ5IG1lYW5zIG9mIHRoZSByZXNpZHVhbHM6CiQkClBfaSA9IGYoRF9pKSArIFxlcHNpbG9uX2kKJCQKCkxldHMgYWRkIGRpc3RhbmNlIHRvIHRoZSBDQkQgYXMgYSBjb3ZhcmlhdGUgdG8geW91ciBkYXRhZnJhbWUuIFdlIHdpbGwgdXNlIEphY2tzb24gU3F1YXJlIGFzIHRoZSBDQkQgb2YgSGFtaWx0b246CmBgYHtyfQp4eV9jYmQgPC0gYygtNzkuODcwOCwgNDMuMjU4NCkgIyJjIiBpcyBhIGZ1bmN0aW9uIHRoYXQgY29tYmluZXMgaXQncyBhcmd1bWVudHMuIEhlcmUsIHdlIGFyZSBhZGRpbmcgY29vcmRpYXRlcyBvZiBKYWNrc29uIFNxdWFyZSBhcyBhIHZlY3RvciB0byB0aGUgZGF0YWZyYW1lLiAKYGBgCgpDb252ZXJ0IHRoZXNlIGNvb3JkaW5hdGVzIHRvIGBzZmAsIHdpdGggRVBTRyBjb2RlIDQzMjYgZm9yIGxvbmdpdHVkZSBhbmQgbGF0aXR1ZGU6CmBgYHtyfQojeHlfY2JkIDwtIHN0X2FzX3NmKHh5X2NiZCwgY29vcmRzID0gYygibG9uIiwgImxhdCIpLCBjcnMgPSA0MzI2KQpgYGAKCmBgYHtyfQp4eV9jdCA8LSBjb29yZGluYXRlcyhzcFRyYW5zZm9ybShIYW1pbHRvbl9DVC5zcCwgQ1JTb2JqID0gIitwcm9qPWxvbmdsYXQgK2RhdHVtPVdHUzg0ICtub19kZWZzIikpCiN3ZSBuZWVkIHRvIHJldHJpZXZlIHRoZSBzcGF0aWFsIGNvb3JkaW5hdGVzIG9mIEhhbWlsdG9uX2N0IGJ5IHVzaW5nICdjb29yZGluYXRlcycgCiN3ZSB1c2UgJ3NwVHJhbnNmb3JtJyBhcyBhIG1ldGhvZCBmb3IgbWFwIHByb2plY3Rpb24gYnkgbWVhbnMgb2YgbGF0aXR1ZGUsIGxvZ2l0dWRlIGFuZCBkYXR1bSBvZiBXR1M4NC4gUmVtZW1iZXIsIHlvdSBjYW4gYWx3YXlzIGxlYXJuIG1vcmUgYWJvdXQgdGhlIHByb3BlcnRpZXMgb2YgYSBmdW5jdGlvbiBieSBpbnN0ZXJ0aW5nIGEgcXVlc3Rpb24gbWFyayBiZWZvcmUgaW4gZnJvbnQgb2YgdGhlIGZ1bmN0aW9uIChpLmUgP2Nvb3JkaW5hdGVzKQoKYGBgCgpHaXZlbiB0aGVzZSBjb29yZGluYXRlcywgdGhlIGZ1bmN0aW9uIGBzZjo6c3RfZGlzdGFuY2VgIGNhbiBiZSB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgZ3JlYXQgY2lyY2xlIGRpc3RhbmNlIGJldHdlZW4gdGhlIGNlbnRyb2lkcyBvZiB0aGUgY2Vuc3VzIHRyYWN0cyBhbmQgSGFtaWx0b24ncyBDQkQuIENhbGwgdGhpcyBgZGlzdDJjYmQuc2xgLCBpLmUuLCBzdHJhaWdodCBsaW5lIGRpc3RhbmNlIHRvIENCRCBpbiBhIHN0cmFpZ2h0OgpgYGB7cn0KZGlzdDJjYmQuc2wgPC0gZGlzdEdlbyh4eV9jdCwgeHlfY2JkKSAjWW91IGNhbiB1c2UgJ2Rpc3RHZW8nIHRvIGNhbGN1bGF0ZSB0aGUgc2hvcnRlc3QgZGlzdGFuY2UgYmV0d2VlbiB0d28gcG9pbnRzIG9uIGEgY2VudHJvaWQgCgpgYGAKCkFkZCB0aGUgZGlzdGFuY2UgdG8gYEhhbWlsdG9uX0NUYCBmb3IgYW5hbHlzaXM6CmBgYHtyfQpIYW1pbHRvbl9DVCRkaXN0LnNsIDwtIGRpc3QyY2JkLnNsCmBgYAoKUmVncmVzc2lvbiBhbmFseXNpcyBpcyBpbXBsZW1lbnRlZCBpbiBgUmAgYnkgbWVhbnMgb2YgdGhlIGBsbWAgZnVuY3Rpb24uIFRoZSBhcmd1bWVudHMgb2YgdGhlIG1vZGVsIGluY2x1ZGUgYW4gb2JqZWN0IG9mIHR5cGUgImZvcm11bGEiIGFuZCBhIGRhdGFmcmFtZS4gT3RoZXIgYXJndW1lbnRzIGluY2x1ZGUgY29uZGl0aW9ucyBmb3Igc3Vic2V0dGluZyB0aGUgZGF0YSwgdXNpbmcgc2FtcGxpbmcgd2VpZ2h0cywgYW5kIHNvIG9uLgoKQSBmb3JtdWxhIGlzIHdyaXR0ZW4gaW4gdGhlIGZvcm0gYHkgfiB4MSArIHgyYCwgYW5kIG1vcmUgY29tcGxleCBleHByZXNzaW9ucyBhcmUgcG9zc2libGUgdG9vLCBhcyB3ZSB3aWxsIHNlZSBiZWxvdy4gRm9yIHRoZSB0aW1lIGJlaW5nLCB0aGUgZm9ybXVsYSBpcyBzaW1wbHkgYFBPUF9ERU5TSVQgfiBkaXN0LnNsYDoKYGBge3J9Cm1vZGVsMSA8LSBsbShmb3JtdWxhID0gUE9QX0RFTlNJVFkgfiBkaXN0LnNsLCBkYXRhID0gSGFtaWx0b25fQ1QpCnN1bW1hcnkobW9kZWwxKSAjJ2xtJyBpcyBhIGZvcm11bGEgd2UgdXNlIGZvciByZWdyZXNzaW9uIGFuYWx5c2lzIGluIHRoaXMgY2hhcHRlci4gUmVjYWxsIHRoYXQgJ2Rpc3Quc2wnIHdhcyByZW5hbWVkIGZyb20gJ2Rpc3QyY2JkLnNsJywgYW5kIGlzIHRoZSBkaXN0YW5jZSBmcm9tIHRoZSBDQkQgKGphY2tzb24gc3F1YXJlKQpgYGAKClRoZSB2YWx1ZSBvZiB0aGUgZnVuY3Rpb24gaXMgYW4gb2JqZWN0IG9mIGNsYXNzIGBsbWAgdGhhdCBjb250YWlucyB0aGUgcmVzdWx0cyBvZiB0aGUgZXN0aW1hdGlvbiwgaW5jbHVkaW5nIHRoZSBjb2VmZmljaWVudHMgd2l0aCB0aGVpciBzaWduaWZpY2FuY2UgZGlhZ25vc3RpY3MsIGFuZCB0aGUgY29lZmZpY2llbnQgb2YgbXVsdGlwbGUgZGV0ZXJtaW5hdGlvbiwgYW1vbmcgb3RoZXIgaXRlbXMuCgpOb3RpY2UgaG93IHRoZSBjb2VmZmljaWVudCBmb3IgZGlzdGFuY2UgaXMgbmVnYXRpdmUgKGFuZCBzaWduaWZpY2FudCkuIFRoaXMgaW5kaWNhdGVzIHRoYXQgcG9wdWxhdGlvbiBkZW5zaXR5IGRlY2xpbmVzIHdpdGggaW5jcmVhc2luZyBkaXN0YW5jZToKJCQKUF9pID0gZihEX2kpICsgXGVwc2lsb25faSA9IDQ0MDUuMTU0MTQgLSAwLjE3OTg5RF9pICsgXGVwc2lsb25faQokJAoKIyMgQXV0b2NvcnJlbGF0aW9uIGFzIGEgTW9kZWwgRGlhZ25vc3RpYwoKTGV0IHF1aWNrbHkgZXhwbG9yZSB0aGUgZml0IG9mIHRoZSBtb2RlbC4gVGhlIHNjYXR0ZXJwbG90IGlzIG9mIHRoZSBhY3R1YWwgcG9wdWxhdGlvbiBkZW5zaXR5IGFuZCB0aGUgZGlzdGFuY2UgdG8gQ0JELCB3aGVyZWFzIHRoZSBibHVlIGxpbmUgaXMgdGhlIHJlZ3Jlc3Npb24gbW9kZWw6CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IEhhbWlsdG9uX0NULCBhZXMoeCA9IGRpc3Quc2wsIHkgPSBQT1BfREVOU0lUWSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2FibGluZShzbG9wZSA9IG1vZGVsMSRjb2VmZmljaWVudHNbMl0sICNSZWNhbGwgdGhhdCAnZ2VvbV9hYmxpbmUnIGNyZWF0ZXMgYSByZWZlcmVuY2UgbGluZSwgaGVyZSBpdCBpcyBmb3IgdGhlIGJsdWUgcmVncmVzc2lvbiBsaW5lIG9uIHRoZSBwbG90LiAKICAgICAgICAgICAgICBpbnRlcmNlcHQgPSBtb2RlbDEkY29lZmZpY2llbnRzWzFdLCAKICAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApCgpgYGAKCkNsZWFybHksIHRoZXJlIGlzIGEgZmFpciBhbW91bnQgb2Ygbm9pc2UgKHRoZSBzY2F0dGVyIG9mIHRoZSBkb3RzIGFyb3VuZCB0aGUgcmVncmVzc2lvbiBsaW5lKS4gSW4gdGhpcyBjYXNlLCB0aGUgcmVncmVzc2lvbiBsaW5lIGNhcHR1cmVzIHRoZSBnZW5lcmFsIHRyZW5kIG9mIHRoZSBkYXRhLCBidXQgdW5kZXJlc3RpbWF0ZXMgbW9zdCBvZiB0aGUgaGlnaCBwb3B1bGF0aW9uIGRlbnNpdHkgYXJlYXMsIGFuZCBvdmVyZXN0aW1hdGVzIG1vc3Qgb2YgdGhlIGxvdyBwb3B1bGF0aW9uIGFyZWFzLgoKSWYgdGhlIHBhdHRlcm4gb2YgdW5kZXItIGFuZCBvdmVyLWVzdGltYXRpb24gaXMgcmFuZG9tIChpLmUuLCB0aGUgcmVzaWR1YWxzIGFyZSByYW5kb20pLCB0aGF0IHdvdWxkIGluZGljYXRlIHRoYXQgdGhlIG1vZGVsIHN1Y2Nlc3NmdWxseSByZXRyaWV2ZWQgYWxsIHRoZSBzeXN0ZW1hdGljIHBhdHRlcm4uIElmIHRoZSBwYXR0ZXJuIGlzIG5vdCByYW5kb20sIHRoZXJlIGlzIGEgdmlvbGF0aW9uIG9mIGFzc3VtcHRpb24gb2YgaW5kZXBlbmRlbmNlLgoKTGV0cyBhZGQgdGhlIHJlc2lkdWFscyBvZiB0aGUgbW9kZWwgdG8geW91ciBkYXRhZnJhbWVzOgpgYGB7cn0KSGFtaWx0b25fQ1QkbW9kZWwxLmUgPC0gbW9kZWwxJHJlc2lkdWFscyAjV2UgYXJlIGNhbGxpbmcgdGhlIHJlc2lkdWFscyBmcm9tICdtb2RlbDEnIApgYGAKCkFuZCBub3cgd2UgY2FuIGNyZWF0ZSBhIG1hcCBvZiB0aGUgcmVzaWR1YWxzLCB3aXRoIHJlZCBpbmRpY2F0aW5nIG5lZ2F0aXZlIHJlc2lkdWFscyBhbmQgYmx1ZSBwb3NpdGl2ZToKYGBge3IgbWVzc2FnZSA9IEZBTFNFfQogIHBsb3RfbHkoSGFtaWx0b25fQ1QpICU+JSAjUmVjYWxsICdwbG90Lmx5JyBpcyBhbiBpbnRlcmFjdGl2ZSBtYXBwaW5nIHRvb2wKICAgIGFkZF9zZihjb2xvciA9IH4obW9kZWwxLmUgPiAwKSwgY29sb3JzID0gYygicmVkIiwgImRvZGdlcmJsdWU0IikpICNZb3UgY2FuIGFkZCBjaGFyYWN0ZXJpc3RpY3MgdG8gYSBwbG90bHkgbWFwIGJ5IG1lYW5zIG9mIHRoZSAnYWRkX3NmJyBmdW5jdGlvbi4gVGhpcyBleGFtcGxlIGFkZHMgY29sb3VycyB0byByZXByZXNlbnQgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHJlc2lkdWFscwpgYGAKCkRvZXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiByZXNpZHVhbHMgbG9vayByYW5kb20/CgpGb3J0dW5hdGVseSwgd2UgYWxyZWFkeSBoYXZlIHRoZSB0b29scyB0byBoZWxwIHVzIHdpdGggdGhpcyBxdWVzdGlvbi4gTGV0cyBjcmVhdGUgYSBzZXQgb2Ygc3BhdGlhbCB3ZWlnaHRzOgpgYGB7cn0KSGFtaWx0b25fQ1QudyA8LSBuYjJsaXN0dyhwb2x5Mm5iKEhhbWlsdG9uX0NULnNwKSkgI1JlY2FsbCB0aGF0IHdlIGNhbiBjcmVhdGUgYSBsc2l0IG9mIHNwYXRpYWwgd2VpZ2h0cyBvZiBuZWFyZXN0IG5laWdoYm91cnMgdXNpbmcgJ25iMmxpc3R3JyAKI0Fsc28gcmVjYWxsIHRoYXQgeW91IGNhbiB1c2UgJ3BvbHkybmInIHRvIGJ1aWxkIGEgbGlzdCBvZiBzcGF0aWFsIHdlaWdodHMgYmFzZWQgb24gcmVnaW9ucyB3aXRoIGNvbnRpbm91cyBib3VuZGFyaWVzLCBsaWtlIHRoZSBwb2x5Z29ucyBvZiBIYW1pbHRvbiBjZW5zdXMgdHJhY3RzLgpgYGAKCldpdGggdGhpcywgd2UgY2FuIGNhbGN1bGF0ZSBNb3JhbidzIEk6CmBgYHtyfQptb3Jhbi50ZXN0KEhhbWlsdG9uX0NUJG1vZGVsMS5lLCBIYW1pbHRvbl9DVC53KQpgYGAKClRoaXMgc3VwcG9ydHMgdGhlIHZpc3VhbCBpbnNwZWN0aW9uIG9mIHRoZSBtYXAsIHNpbmNlIHRoZSB0ZXN0IG9mIGh5cG90aGVzaXMgcmVqZWN0cyB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIGluZGVwZW5kZW5jZSBhdCBhIHZlcnkgaGlnaCBsZXZlbCBvZiBjb25maWRlbmNlIChub3RlIHRoZSBleHRyZW1lbHkgc21hbGwgdmFsdWUgb2YgcCkuCgpTcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiwgYXMgbWVudGlvbmVkIGFib3ZlLCBpcyBhIHZpb2xhdGlvbiBvZiBhIGtleSBhc3N1bXB0aW9uIG9mIGxpbmVhciByZWdyZXNzaW9uLCBhbmQgbGlrZWx5IHRoZSBjb25zZXF1ZW5jZSBvZiBhIG1vZGVsIHRoYXQgd2FzIG5vdCBjb3JyZWN0bHkgc3BlY2lmaWVkLCBlaXRoZXIgYmVjYXVzZSB0aGUgZnVuY3Rpb25hbCBmb3JtIHdhcyBpbmNvcnJlY3QgKGUuZy4sIHRoZSByZWxhdGlvbnNoaXAgd2FzIG5vdCBsaW5lYXIpLCBvciB0aGVyZSBhcmUgbWlzc2luZyBjb3ZhcmlhdGVzLgoKTGV0cyBleHBsb3JlIHRoZSBmaXJzdCBvZiB0aGVzZSBwb3NzaWJpbGl0aWVzIGJ5IG1lYW5zIG9mIHZhcmlhYmxlIHRyYW5zZm9ybWF0aW9ucy4KCiMjIFZhcmlhYmxlIFRyYW5zZm9ybWF0aW9ucwoKVGhlIHRlcm0gbGluZWFyIHJlZ3Jlc3Npb24gcmVmZXJzIHRvIHRoZSBsaW5lYXJpdHkgaW4gdGhlIGNvZWZmaWNpZW50cy4gVmFyaWFibGUgdHJhbnNmb3JtYXRpb25zIGFsbG93IHlvdSB0byBjb25zaWRlciBub24tbGluZWFyIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBjb3ZhcmlhdGVzLCB3aGlsZSBzdGlsbCBwcmVzZXJ2aW5nIHRoZSBsaW5lYXJpdHkgb2YgdGhlIGNvZWZmaWNpZW50cy4KCkZvciBpbnN0YW5jZSwgYSBwb3NzaWJsZSB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgdmFyaWFibGUgZGlzdGFuY2UgY291bGQgYmUgYXMgaW52ZXJzZSBkaXN0YW5jZToKJCQKZihEX2kpID0gXGJldGFfMCArIFxiZXRhXzFcZnJhY3sxfXtEX2l9CiQkCiAKTGV0cyBjcmVhdGUgYSBuZXcgY292YXJpYXRlIHRoYXQgaXMgdGhlIGludmVyc2UgZGlzdGFuY2U6CmBgYHtyfQpIYW1pbHRvbl9DVCA8LSBtdXRhdGUoSGFtaWx0b25fQ1QsIGludmRpc3Quc2wgPSAxL2Rpc3Quc2wpCiNSZWNhbGwgdGhhdCBtdXRhdGUgYWRkcyB0byBleGlzdGluZyB2YXJpYWJsZXMgd2hpbGUgcHJlc2VydmluZyB0aG9zZSB0aGF0IGFscmVhZHkgZXhpc3QuIFdlIGFyZSBtdXRhdGluZyBIYW1pbHRvbiBjZW5zdXMgdHJhdCBpbmZvcm1hdGlvbiB0byBjcmVhdGUgaW52ZXJzZSBkaXN0YW5jZQpgYGAKClVzZSBpbnZlcnNlIGRpc3RhbmNlIGFzIHRoZSBjb3ZhcmlhdGUgaW4gdGhlIG1vZGVsOgpgYGB7cn0KbW9kZWwyIDwtIGxtKGZvcm11bGEgPSBQT1BfREVOU0lUWSB+IGludmRpc3Quc2wsIGRhdGEgPSBIYW1pbHRvbl9DVCkKc3VtbWFyeShtb2RlbDIpICNOb3RjZSBob3cgdGhlIG5ldyAnbW9kZWwyJyB1c2VzIHRoZSBpbnZlcnNlIGRpc3RhbmNlIGZyb20gdGhlIENCRCByYXRoZXIgdGhhbiB0aGUgb3JpZ2luYWwuIFRoaXMgaXMgYmVjYXVzZSAnaW52ZGlzdC5zbCcgaXMgdXNlZCBpbiB0aGlzIG1vZGVsIGluc3RlYWQhCmBgYAoKQXMgdGhlIHNjYXR0ZXJwbG90IGJlbG93IHNob3dzICh0aGUgbW9kZWwgaXMgdGhlIGJsdWUgbGluZSksIHdlIGNhbiBjYXB0dXJlIGEgbm9uLWxpbmVhciByZWxhdGlvbnNoaXAsIHdoaWNoIGRvZXMgYSBiZXR0ZXIgam9iIG9mIGRlc2NyaWJpbmcgdGhlIGhpZ2ggZGVuc2l0eSBvZiB0cmFjdHMgY2xvc2UgdG8gdGhlIENCRCwgYnV0IHVuZm9ydHVuYXRlbHkgaXMgYSBwb29yIGRlc2NyaXB0aW9uIG9mIGRlbnNpdHkgYWxtb3N0IGV2ZXJ5d2hlcmUgZWxzZToKYGBge3J9CmdncGxvdChkYXRhID0gSGFtaWx0b25fQ1QsIGFlcyh4ID0gZGlzdC5zbCwgeSA9IFBPUF9ERU5TSVRZKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfZnVuY3Rpb24oZnVuPWZ1bmN0aW9uKHgpMjI5OS42ICsgMjI2MDUyMS41L3gsIGdlb209ImxpbmUiLCBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApCmBgYAoKQWRkIHRoZSByZXNpZHVhbHMgb2YgdGhpcyBtb2RlbCB0byB0aGUgZGF0YWZyYW1lIGZvciBmdXJ0aGVyIGV4YW1pbmF0aW9uOgpgYGB7cn0KSGFtaWx0b25fQ1QkbW9kZWwyLmUgPC0gbW9kZWwyJHJlc2lkdWFscwpgYGAKCklmIHdlIGNhbGN1bGF0ZSBNb3JhbidzIEksIHlvdSB3aWxsIG5vdGljZSB0aGF0IHRoZSBjb2VmZmljaWVudCBpcyBsb3dlciB0aGFuIGZvciB0aGUgcHJldmlvdXMgbW9kZWwgYnV0IHN0aWxsIHNpZ25pZmljYW50LCB3aWNoIG1lYW5zIHRoYXQgdGhlIHJlc2lkdWFscyBhcmUgbm90IHJhbmRvbToKYGBge3J9Cm1vcmFuLnRlc3QoSGFtaWx0b25fQ1QkbW9kZWwyLmUsIEhhbWlsdG9uX0NULncpCmBgYAoKVGhlIGxpdGVyYXR1cmUgb24gcG9wdWxhdGlvbiBkZW5zaXR5IGdyYWRpZW50cyBzdWdnZXN0cyBvdGhlciBub24tbGluZWFyIHRyYW5zZm9ybWF0aW9ucywgaW5jbHVkaW5nOgokJApmKERfaSkgPSBleHAoXGJldGFfMClleHAoXGJldGFfMXhfaSkKJCQKClRoaXMgZnVuY3Rpb24gaXMgbm8gbG9uZ2VyIGxpbmVhciBpbiB0aGUgY29lZmZpY2llbnRzIChzaW5jZSBhbGwgdGhlIGNvZWZmaWNpZW50c2FyZSB0cmFuc2Zvcm1lZCBieSB0aGUgZXhwb25lbnRpYWwpLiBGb3J0dW5hdGVseSwgdGhlcmUgaXMgYSBzaW1wbGUgd2F5IG9mIGNoYW5naW5nIHRoaXMgdG8gYSBsaW5lYXIgZXhwcmVzc2lvbiwgYnkgdGFraW5nIHRoZSBsb2dhcml0aG0gb24gYm90aCBzaWRlcyBvZiB0aGUgZXF1YXRpb246CiQkCmxuKFBfaSkgPSBcYmV0YV8wICsgXGJldGFfMXhfaSArIFxlcHNpbG9uX2kKJCQKCkxldHMgY3JlYXRlIHRoZXNlIGNvdmFyaWF0ZXM6CmBgYHtyfQpIYW1pbHRvbl9DVCA8LSBtdXRhdGUoSGFtaWx0b25fQ1QsIGxuUE9QX0RFTiA9IGxvZyhQT1BfREVOU0lUWSkpICNXZSBhcmUgbXV0YXRpbmcgcG9wdWxhdGlvbiBkZW5zaXR5IGJ5IHRha2luZyBpdHMgbmF0dXJhbCBsb2dhcml0aG0gb2YgYm90aCBzaWRlcyBvZiB0aGUgZXF1YXRpb24uIFRoaXMgY2hhbmdlcyB0aGUgY29lZmZpY2llbnRzIGJhY2sgdG8gYSBsaW5lYXIgZXhwcmVzc2lvbi4KYGBgCgpBbmQgZXN0aW1hdGUgdGhlIG1vZGVsOgpgYGB7cn0KbW9kZWwzIDwtIGxtKGZvcm11bGEgPSBsblBPUF9ERU4gfiBkaXN0LnNsLCBkYXRhID0gSGFtaWx0b25fQ1QpCnN1bW1hcnkobW9kZWwzKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IEhhbWlsdG9uX0NULCBhZXMoeCA9IGRpc3Quc2wsIHkgPSBQT1BfREVOU0lUWSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X2Z1bmN0aW9uKGZ1bj1mdW5jdGlvbih4KWV4cCg4LjQ2NSAtIDAuMDAwMTE2MSAqIHgpLCAKICAgICAgICAgICAgICAgIGdlb209ImxpbmUiLCBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDEpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApCmBgYAoKQXMgYmVmb3JlLCBhZGQgdGhlIHJlc2lkdWFscyBvZiB0aGUgbW9kZWwgdG8gdGhlIGRhdGFmcmFtZSBmb3IgZnVydGhlciBleGFtaW5hdGlvbjoKYGBge3J9CkhhbWlsdG9uX0NUJG1vZGVsMy5lIDwtIG1vZGVsMyRyZXNpZHVhbHMKYGBgCgpXaGlsZSB0aGlzIGxhdGVzdCBtb2RlbCBwcm92aWRlcyBhIHNvbWV3aGF0IGJldHRlciBmaXQsIHRoZXJlIGlzIHN0aWxsIHN5c3RlbWF0aWMgdW5kZXItIGFuZCBvdmVyLXByZWRpY3Rpb24sIGFzIHNlZW4gaW4gdGhlIG1hcCBiZWxvdyAocmVkIGFyZSBuZWdhdGl2ZSByZXNpZHVhbHMgYW5kIGJsdWUgYXJlIHBvc2l0aXZlKToKYGBge3IgbWVzc2FnZSA9IEZBTFNFfQogIHBsb3RfbHkoSGFtaWx0b25fQ1QpICU+JQogICAgYWRkX3NmKGNvbG9yID0gfihtb2RlbDMuZSA+IDApLCBjb2xvcnMgPSBjKCJyZWQiLCAiZG9kZ2VyYmx1ZTQiKSkKYGBgCgpNb3JhbidzIEkgYXMgd2VsbCBzdHJvbmdseSBzdWdnZXN0cyB0aGF0IHRoZSByZXNpZHVhbHMgYXJlIG5vdCBpbmRlcGVuZGVudDoKYGBge3J9Cm1vcmFuLnRlc3QoSGFtaWx0b25fQ1QkbW9kZWwzLmUsIEhhbWlsdG9uX0NULncpCmBgYAoKIyMgQSBOb3RlIGFib3V0IFNwYXRpYWwgQXV0b2NvcnJlbGF0aW9uIGluIFJlZ3Jlc3Npb24gQW5hbHlzaXMKClNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIHdhcyBvcmlnaW5hbGx5IHNlZW4gYXMgYSBwcm9ibGVtIGluIHJlZ3Jlc3Npb24gYW5hbHlzaXMuIEl0IGlzIG5vdCBkaWZmaWN1bHQgdG8gc2VlIHdoeSwgYWZ0ZXIgdGVzdGluZyB0aHJlZSBtb2RlbHMgaW4gdGhpcyBjaGFwdGVyLgoKSSBwcmVmZXIgdG8gdmlldyBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBhcyBhbiBvcHBvcnR1bml0eSBmb3IgZGlzY292ZXJ5LiBGb3IgaW5zdGFuY2UsIHRoZSBtb2RlbHMgYWJvdmUgYWxsIHNlZW0gdG8gc3RydWdnbGUgY2FwdHVyaW5nIHRoZSBsYXJnZSB2YXJpYXRpb25zIGluIHBvcHVsYXRpb24gZGVuc2l0eSBiZXR3ZWVuIHRoZSBjZW50cmFsIHBhcnRzIG9mIHRoZSBjaXR5IGFuZCB0aGUgc3VidXJicyBvZiBIYW1pbHRvbi4gUGVyaGFwcyB0aGlzIGNvdWxkIGJlIGR1ZSB0byBhIF9yZWdpbWUgY2hhbmdlXywgb3IgaW4gb3RoZXIgd29yZHMsIHRoZSBwcmVzZW5jZSBvZiBhbiB1bmRlcmx5aW5nIHByb2Nlc3MgdGhhdCBvcGVyYXRlcyBzb21ld2hhdCBkaWZmZXJlbnRseSBpbiBkaWZmZXJlbnQgcGFydHMgcGFydHMgb2YgdGhlIGNpdHkuIFRoZSBsYXN0IG1vZGVsLCBmb3IgaW5zdGFuY2UsIHN1Z2dlc3RzIHRoYXQgdGhlIGNsb3NlIHByb3hpbWl0eSBvZiBCdXJsaW5ndG9uIG1pZ2h0IGhhdmUgYW4gZWZmZWN0LgoKVGhlIGFuYWx5c2lzIHRoYXQgZm9sbG93cyBpcyBzb21ld2hhdCBtb3JlIGFkdmFuY2VkLCBidXQgc2VydmVzIHRvIGlsbHVzdHJhdGUgdGhlIGlkZWEgb2Ygc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gYXMgYSB0b29sIGZvciBkaXNjb3ZlcnkuCgpMZXRzIGJlZ2luIGJ5IGNyZWF0aW5nIGxvY2FsIE1vcmFuIG1hcHMgdG8gaWRlbnRpZnkgcG90ZW50aWFsIHNwYXRpYWwgcmVnaW1lczogCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpsb2NhbG1vcmFuLm1hcChIYW1pbHRvbl9DVCwgSGFtaWx0b25fQ1QudywgIlBPUF9ERU5TSVRZIiwgYnkgPSAiVFJBQ1QiKQpgYGAKCkV4YW1pbmF0aW9uIG9mIHRoZSBtYXAgYWJvdmUsIHN1Z2dlc3RzIHRoYXQgdGhlcmUgYXJlIHBvc3NpYmx5IHRocmVlIHJlZ2ltZXM6IGEgQ0JEICgiSEgiIGFuZCBzaWduaWZpY2FudCB0cmFjdHMpLCBTdWJ1cmJzICgiTEwiIGFuZCBzaWduaWZpY2FudCB0cmFjdHMpLCBhbmQgT3RoZXIgKG5vdCBzaWduaWZpY2FudCB0cmFjdHMpLiBCYXNlZCBvbiB0aGlzLCB3ZSB3aWxsIGNyZWF0ZSB0d28gaW5kaWNhdG9yIHZhcmlhYmxlcywgb25lIGZvciBjZW5zdXMgdHJhY3RzIGluIHRoZSBDQkQgYW5kIGFub3RoZXIgZm9yIGNlbnN1cyB0cmFjdHMgaW4gdGhlIFN1YnVyYnMuIEFuIGluZGljYXRvciB2YXJpYWJsZSB0YWtlcyB2YWx1ZXMgb2YgMSBvciB6ZXJvLCBkZXBlbmRpbmcgb24gd2hldGhlciBhIGNvbmRpdGlvbiBpcyB0cnVlLiBGb3IgaW5zdGFuY2UsIGFsbCBjZW5zdXMgdHJhY3RzIGluIHRoZSBDQkQgd2lsbCB0YWtlIHRoZSB2YWx1ZSBvZiAxIGluIHRoZSBDQkQgaW5kaWNhdG9yIHZhcmlhYmxlLCBhbmQgYWxsIG90aGVycyB3aWxsIGJlIHplcm8uCgpCZWdpbiBieSBjb21wdXRpbmcgdGhlIGxvY2FsIHN0YXRpc3RpY3M6CmBgYHtyfQpQT1BfREVOLmxtIDwtIGxvY2FsbW9yYW4oSGFtaWx0b25fQ1QkUE9QX0RFTlNJVFksIGxpc3R3ID0gSGFtaWx0b25fQ1QudykKYGBgCgpOZXh0LCBpZGVudGlmeSB0aGUgdHlwZSBvZiB0cmFjdCBiYXNlZCBvbiB0aGUgc3BhdGlhbCByZWxhdGlvbnNoaXBzIChpLmUuLCAiSEgiLCAiTEwiLCBvciAiSEwvTEgiKS4gQWZ0ZXIgdGhhdCwgaWRlbnRpZnkgYXMgQ0JEIGFsbCB0cmFjdHMgZm9yIHdoaWNoIFR5cGUgaXMgIkhIIiBhbmQgdGhlIHAtdmFsdWUgaXMgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDAuMDUuIExpa2V3aXNlLCBpZGVudGlmeSBhcyBTdWJ1cmIgYWxsIHRyYWN0cyBmb3Igd2hpY2ggVHlwZSBpcyAiTEwiIGFuZCB0aGUgcC12YWx1ZSBpcyBhbHNvIGxlc3MgdGhhbiBvciBlcXVhbCB0byAwLjA1OgpgYGB7cn0KZGZfbXNjIDwtIHRyYW5zbXV0ZShIYW1pbHRvbl9DVCwKICAgICAgICAgICAgICAgICAgICBUUkFDVCA9IFRSQUNULAogICAgICAgICAgICAgICAgICAgIFogPSAoUE9QX0RFTlNJVFkgLSBtZWFuKFBPUF9ERU5TSVRZKSkgLyB2YXIoUE9QX0RFTlNJVFkpLAogICAgICAgICAgICAgICAgICAgIFNNQSA9IGxhZy5saXN0dyhIYW1pbHRvbl9DVC53LCBaKSwKICAgICAgICAgICAgICAgICAgICBUeXBlID0gZmFjdG9yKGlmZWxzZShaIDwgMCAmIFNNQSA8IDAsICJMTCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFogPiAwICYgU01BID4gMCwgIkhIIiwgIkhML0xIIikpKSkKZGZfbXNjIDwtIGNiaW5kKGRmX21zYywgUE9QX0RFTi5sbSkKCkNCRCA8LSBpZmVsc2UoZGZfbXNjJFR5cGUgPT0gIkhIIiAmIGRmX21zYyRgUHIuei4uLjBgIDwgMC4wNSwgMSwgMCkKU3VidXJiIDwtIGlmZWxzZShkZl9tc2MkVHlwZSA9PSAiTEwiICYgZGZfbXNjJGBQci56Li4uMGAgPCAwLjA1LCAxLCAwKQpgYGAKCkFkZCB0aGUgaW5kaWNhdG9yIHZhcmlhYmxlcyB0byB0aGUgZGF0YWZyYW1lOgpgYGB7cn0KSGFtaWx0b25fQ1QkQ0JEIDwtIENCRApIYW1pbHRvbl9DVCRTdWJ1cmIgPC0gU3VidXJiCmBgYAoKVGhlIG1vZGVsIHRoYXQgSSBwcm9wb3NlIHRvIGVzdGltYXRlIGlzIGEgdmFyaWF0aW9uIG9mIHRoZSBsYXN0IG5vbi1saW5lYXIgc3BlY2lmaWNhdGlvbiwgYnV0IHdpdGggX3JlZ2ltZSBicmVha3NfOgokJApsbihQX2kpID0gXGJldGFfMCArIFxiZXRhXzF4X2kgKyBcYmV0YV8yQ0JEX2kgKyBcYmV0YV8zU3VidXJiX2kgKyBcYmV0YV80Q0JEX2l4X2kgKyBcYmV0YV81U3VidXJiX2l4X2kgKyBcZXBzaWxvbl9pCiQkCgpTaW5jZSB0aGUgaW5kaWNhdG9yIHZhcmlhYmxlcyBmb3IgQ0JEIGFuZCBTdWJ1cmIgdGFrZSB2YWx1ZXMgb2YgemVybyBhbmQgb25lLCBlZmZlY3RpdmVseSB3ZSBoYXZlIHRoZSBmb2xsb3dpbmc6CiQkCmxuKFBfaSk9XEJpZ2dce1xiZWdpbnthcnJheX17bCBsfQooXGJldGFfMCArIFxiZXRhXzIpICsgKFxiZXRhXzEgKyBcYmV0YV8yKXhfaSArIFxlcHNpbG9uX2kgXHRleHR7IGlmIGNlbnN1cyB0cmFjdCB9IGkgXHRleHR7IGlzIGluIHRoZSBDQkR9XFwKKFxiZXRhXzAgKyBcYmV0YV8zKSArIChcYmV0YV8xICsgXGJldGFfNSl4X2kgKyBcZXBzaWxvbl9pIFx0ZXh0eyBpZiBjZW5zdXMgdHJhY3QgfSBpIFx0ZXh0eyBpcyBpbiB0aGUgU3VidXJic31cXApcYmV0YV8wICArIFxiZXRhXzF4X2kgKyBcZXBzaWxvbl9pIFx0ZXh0eyBvdGhlcndpc2V9XFwKXGVuZHthcnJheX0KJCQKCgpOb3RpY2UgaG93IHRoZSBtb2RlbCBub3cgYWxsb3dzIGZvciBkaWZmZXJlbnQgc2xvcGVzIGFuZCBpbnRlcmNlcHRzIGZvciBvYnNlcnZhdGlvbnMgaW4gZGlmZmVyZW50IHBhcnRzIG9mIHRoZSBjaXR5LiBFc3RpbWF0ZSB0aGUgbW9kZWw6CmBgYHtyfQptb2RlbDQgPC0gbG0oZm9ybXVsYSA9IGxuUE9QX0RFTiB+IGRpc3Quc2wgKyBDQkQgKiBkaXN0LnNsICsgU3VidXJiICogZGlzdC5zbCwKICAgICAgICAgICAgIGRhdGEgPSBIYW1pbHRvbl9DVCkKc3VtbWFyeShtb2RlbDQpCmBgYAoKVGhlIG1vZGVsIGlzIGEgbXVjaCBiZXR0ZXIgZml0IChzZWUgdGhlIHRoZSBjb2VmZmljaWVudCBvZiBtdWx0aXBsZSBkZXRlcm1pbmF0aW9uKS4KCkxldHMgZXhhbWluZSB0aGUgcmVzaWR1YWxzOgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KSGFtaWx0b25fQ1QkbW9kZWw0LmUgPC0gbW9kZWw0JHJlc2lkdWFscwpwbG90X2x5KEhhbWlsdG9uX0NUKSAlPiUKICBhZGRfc2YoY29sb3IgPSB+KG1vZGVsNC5lID4gMCksIGNvbG9ycyA9IGMoInJlZCIsICJkb2RnZXJibHVlNCIpKQpgYGAKCkl0IGlzIG5vdCBjbGVhciBmcm9tIHRoZSB2aXN1YWwgaW5zcGVjdGlvbiB0aGF0IHRoZSByZXNpZHVhbHMgYXJlIGluZGVwZW5kZW50LCBidXQgdGhpcyBjYW4gYmUgdGVzdGVkIGFzIHVzdWFsIGJ5IG1lYW5zIG9mIE1vcmFuJ3MgSSBjb2VmZmljaWVudDoKYGBge3J9Cm1vcmFuLnRlc3QoSGFtaWx0b25fQ1QkbW9kZWw0LmUsIEhhbWlsdG9uX0NULncpCmBgYAoKQmFzZWQgb24gdGhlIHJlc3VsdHMsIHdlIGZhaWwgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMsIGFuZCBjYW4gYmUgY29uZmlkZW50IHRoYXQgdGhlIHJlc2lkdWFscyBhcmUgbGlrZWx5IHJhbmRvbS4KClRoZSBmb2xsb3dpbmcgZmlndXJlIGlsbHVzdHJhdGVzIHRoZSBmaW5hbCBtb2RlbDoKYGBge3J9CmZ1bi4xIDwtIGZ1bmN0aW9uKHgpZXhwKDcuOTQzICsgMC45NTM2IC0gKDAuMDAwMDMyNDggICsgMC4wMDAwMjczKSAqIHgpICNDQkQKZnVuLjIgPC0gZnVuY3Rpb24oeClleHAoNy45NDMgLSAwLjk1MzUgLSAoMC4wMDAwMzI0OCArIDAuMDAwMTAwNikgKiB4KSAjU3VidXJiCmZ1bi4zIDwtIGZ1bmN0aW9uKHgpZXhwKDcuOTQzIC0gMC4wMDAwMzI0OCAqIHgpICNPdGhlcgoKZ2dwbG90KGRhdGEgPSBIYW1pbHRvbl9DVCwgYWVzKHggPSBkaXN0LnNsLCB5ID0gUE9QX0RFTlNJVFkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBmaWx0ZXIoSGFtaWx0b25fQ1QsIENCRCA9PSAxKSwgY29sb3IgPSAiUmVkIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGZpbHRlcihIYW1pbHRvbl9DVCwgU3VidXJiID09IDEpLCBjb2xvciA9ICJCbHVlIikgKwogIHN0YXRfZnVuY3Rpb24oZnVuPSBmdW4uMSwgCiAgICAgICAgICAgICAgICBnZW9tPSJsaW5lIiwgc2l6ZSA9IDEsIGFlcyhjb2xvciA9ICJDQkQiKSkgKwogIHN0YXRfZnVuY3Rpb24oZnVuPWZ1bi4yLCAKICAgICAgICAgICAgICAgIGdlb209ImxpbmUiLCBzaXplID0gMSwgYWVzKGNvbG9yID0gIlN1YnVyYiIpKSArCiAgc3RhdF9mdW5jdGlvbihmdW49ZnVuLjMsIAogICAgICAgICAgICAgICAgZ2VvbT0ibGluZSIsIHNpemUgPSAxLCBhZXMoY29sb3IgPSAiT3RoZXIiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCAiYmxhY2siLCAiYmx1ZSIpLCAKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQ0JEIiwgIk90aGVyIiwgIlN1YnVyYiIpKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwKQpgYGA=